Tutustu edistyneisiin TypeScript-tekniikoihin, jotka käyttävät malliliteraaleja tehokkaaseen merkkijonotyyppien manipulointiin. Opi jäsentämään, muuntamaan ja validoimaan merkkijonopohjaisia tyyppejä tehokkaasti.
TypeScriptin malliliteraalien jäsennys: edistynyt merkkijonotyyppien manipulointi
TypeScriptin tyyppijärjestelmä tarjoaa tehokkaita työkaluja datan käsittelyyn ja validointiin käännösaikana. Näistä työkaluista malliliteraalit tarjoavat ainutlaatuisen lähestymistavan merkkijonotyyppien manipulointiin. Tämä artikkeli syventyy malliliteraalien jäsennyksen edistyneisiin puoliin ja esittelee, kuinka luodaan hienostunutta tyyppitason logiikkaa merkkijonopohjaiselle datalle.
Mitä ovat malliliteraalityypit?
Malliliteraalityypit, jotka esiteltiin TypeScript 4.1:ssä, mahdollistavat merkkijonotyyppien määrittelyn merkkijonoliteraalien ja muiden tyyppien perusteella. Ne käyttävät takakenoja (`) tyypin määrittelyyn, samalla tavalla kuin malliliteraalit JavaScriptissä.
Esimerkiksi:
type Color = "red" | "green" | "blue";
type Shade = "light" | "dark";
type ColorCombination = `${Shade} ${Color}`;
// ColorCombination on nyt "light red" | "light green" | "light blue" | "dark red" | "dark green" | "dark blue"
Tämä näennäisesti yksinkertainen ominaisuus avaa laajan valikoiman mahdollisuuksia käännösaikaiseen merkkijonojen käsittelyyn.
Malliliteraalityyppien peruskäyttö
Ennen kuin syvennymme edistyneisiin tekniikoihin, kerrataan muutamia peruskäyttötapauksia.
Merkkijonoliteraalien yhdistäminen
Voit helposti yhdistää merkkijonoliteraaleja ja muita tyyppejä luodaksesi uusia merkkijonotyyppejä:
type Greeting = `Hello, ${string}!`;
// Esimerkkikäyttö
const greet = (name: string): Greeting => `Hello, ${name}!`;
const message: Greeting = greet("World"); // Kelvollinen
const invalidMessage: Greeting = "Goodbye, World!"; // Virhe: Tyyppi '"Goodbye, World!"' ei ole kohdennettavissa tyyppiin '`Hello, ${string}!`'.
Union-tyyppien käyttäminen
Union-tyypit mahdollistavat tyypin määrittelyn useiden mahdollisten arvojen yhdistelmänä. Malliliteraalit voivat hyödyntää union-tyyppejä luodakseen monimutkaisempia merkkijonotyyppien unioneja:
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = `/api/users` | `/api/products`;
type Route = `${HTTPMethod} ${Endpoint}`;
// Route on nyt "GET /api/users" | "POST /api/users" | "PUT /api/users" | "DELETE /api/users" | "GET /api/products" | "POST /api/products" | "PUT /api/products" | "DELETE /api/products"
Edistyneet malliliteraalien jäsennystekniikat
Malliliteraalityyppien todellinen voima piilee niiden kyvyssä yhdistyä muihin TypeScriptin edistyneisiin ominaisuuksiin, kuten ehdollisiin tyyppeihin ja tyypin päättelyyn, merkkijonotyyppien jäsentämiseksi ja manipuloimiseksi.
Merkkijonotyypin osien päättely
Voit käyttää infer-avainsanaa ehdollisessa tyypissä poimiaksesi tiettyjä osia merkkijonotyypistä. Tämä on perusta merkkijonotyyppien jäsentämiselle.
Tarkastellaan tyyppiä, joka poimii tiedostopäätteen tiedostonimestä:
type GetFileExtension = T extends `${string}.${infer Extension}` ? Extension : never;
// Esimerkkejä
type Extension1 = GetFileExtension<"myFile.txt">; // "txt"
type Extension2 = GetFileExtension<"anotherFile.image.jpg">; // "image.jpg" (nappaa viimeisen osan pisteen jälkeen)
type Extension3 = GetFileExtension<"noExtension">; // never
Tässä esimerkissä ehdollinen tyyppi tarkistaa, vastaako syötetyyppi T mallia ${string}.${infer Extension}. Jos vastaa, se päättelee osan viimeisen pisteen jälkeen Extension-tyyppimuuttujaan, joka sitten palautetaan. Muussa tapauksessa se palauttaa never.
Jäsennys useilla päättelyillä
Voit käyttää useitainfer-avainsanoja samassa malliliteraalissa poimiaksesi useita osia merkkijonotyypistä samanaikaisesti.
type ParseConnectionString =
T extends `${infer Protocol}://${infer Host}:${infer Port}` ?
{ protocol: Protocol, host: Host, port: Port } : never;
// Esimerkki
type Connection = ParseConnectionString<"http://localhost:3000">;
// { protocol: "http", host: "localhost", port: "3000" }
type InvalidConnection = ParseConnectionString<"invalid-connection">; // never
Tämä tyyppi jäsentää yhteysmerkkijonon sen protokolla-, isäntä- ja porttikomponentteihin.
Rekursiiviset tyyppimääritykset monimutkaiseen jäsennykseen
Monimutkaisempia merkkijonorakenteita varten voit käyttää rekursiivisia tyyppimäärityksiä. Tämä mahdollistaa merkkijonotyypin osien toistuvan jäsentämisen, kunnes saavutetaan haluttu tulos.
Oletetaan, että haluat jakaa merkkijonon yksittäisten merkkien taulukoksi tyyppitasolla. Tämä on huomattavasti edistyneempää.
type StringToArray =
T extends `${infer Char}${infer Rest}`
? StringToArray
: Acc;
// Esimerkki
type MyArray = StringToArray<"hello">; // ["h", "e", "l", "l", "o"]
Selitys:
StringToArray<T extends string, Acc extends string[] = []>: Tämä määrittelee geneerisen tyypin nimeltäStringToArray, joka ottaa syötteenä merkkijonotyypinTja valinnaisen kerääjänAcc, jonka oletusarvo on tyhjä merkkijonotaulukko. Kerääjä tallentaa merkit käsittelyn aikana.T extends `${infer Char}${infer Rest}`: Tämä on ehdollinen tyyppitarkistus. Se tarkistaa, voidaanko syötemerkkijonoTjakaa ensimmäiseen merkkiinCharja loppuosaanRest.infer-avainsanaa käytetään näiden osien kaappaamiseen.StringToArray<Rest, [...Acc, Char]>: Jos jako onnistuu, kutsumme rekursiivisestiStringToArray-tyyppiä merkkijonon loppuosalla (Rest) ja uudella kerääjällä. Uusi kerääjä luodaan levittämällä olemassa olevaAccja lisäämällä nykyinen merkkiCharloppuun. Tämä käytännössä lisää merkin keräävään taulukkoon.Acc: Jos merkkijono on tyhjä (ehdollinen tyyppi epäonnistuu, mikä tarkoittaa, ettei merkkejä enää ole), palautamme kerätyn taulukonAcc.
Tämä esimerkki osoittaa rekursion voiman merkkijonotyyppien käsittelyssä. Jokainen rekursiivinen kutsu irrottaa yhden merkin ja lisää sen taulukkoon, kunnes merkkijono on tyhjä.
Erottimien kanssa työskentely
Malliliteraaleja voidaan helposti käyttää erottimien kanssa merkkijonojen jäsentämiseen. Oletetaan, että haluat poimia pilkuilla erotetut sanat.
type SplitString =
T extends `${infer First}${D}${infer Rest}`
? [First, ...SplitString]
: [T];
// Esimerkki
type Words = SplitString<"apple,banana,cherry", ",">; // ["apple", "banana", "cherry"]
Tämä tyyppi jakaa merkkijonon rekursiivisesti jokaisen erottimen D kohdalla.
Käytännön sovellukset
Näillä edistyneillä malliliteraalien jäsennystekniikoilla on lukuisia käytännön sovelluksia TypeScript-projekteissa.
Datan validointi
Voit validoida merkkijonopohjaista dataa tiettyjä malleja vastaan käännösaikana. Esimerkiksi sähköpostiosoitteiden, puhelinnumeroiden tai luottokorttinumeroiden validointi. Tämä lähestymistapa antaa aikaista palautetta ja vähentää ajonaikaisia virheitä.
Tässä on esimerkki yksinkertaistetun sähköpostiosoitemuodon validoinnista:
type EmailFormat = `${string}@${string}.${string}`;
const validateEmail = (email: string): email is EmailFormat => {
// Todellisuudessa asianmukaiseen sähköpostin validointiin käytettäisiin paljon monimutkaisempaa säännöllistä lauseketta.
// Tämä on vain esittelytarkoituksessa.
return /.+@.+\..+/.test(email);
}
const validEmail: EmailFormat = "user@example.com"; // Kelvollinen
const invalidEmail: EmailFormat = "invalid-email"; // Tyyppi 'string' ei ole kohdennettavissa tyyppiin '`${string}@${string}.${string}`'.
if(validateEmail(validEmail)) {
console.log("Valid Email");
}
if(validateEmail("invalid-email")) {
console.log("Tätä ei tulosteta.");
}
Vaikka ajonaikainen validointi säännöllisellä lausekkeella on edelleen tarpeen tapauksissa, joissa tyyppitarkistin ei voi täysin valvoa rajoitusta (esim. ulkoista syötettä käsiteltäessä), EmailFormat-tyyppi tarjoaa arvokkaan ensimmäisen puolustuslinjan käännösaikana.
API-päätepisteiden generointi
Malliliteraaleja voidaan käyttää API-päätepistetyyppien luomiseen perus-URL:n ja parametrijoukon perusteella. Tämä auttaa varmistamaan yhdenmukaisuuden ja tyyppiturvallisuuden API-rajapintojen kanssa työskenneltäessä.
type BaseURL = "https://api.example.com";
type Resource = "users" | "products";
type ID = string | number;
type GetEndpoint = `${BaseURL}/${T}/${U}`;
// Esimerkkejä
type UserEndpoint = GetEndpoint<"users", 123>; // "https://api.example.com/users/123"
type ProductEndpoint = GetEndpoint<"products", "abc-456">; // "https://api.example.com/products/abc-456"
Koodin generointi
Edistyneemmissä skenaarioissa malliliteraalityyppejä voidaan käyttää osana koodin generointiprosesseja. Esimerkiksi SQL-kyselyiden generointi skeeman perusteella tai käyttöliittymäkomponenttien luominen konfiguraatiotiedoston pohjalta.
Kansainvälistäminen (i18n)
Malliliteraalit voivat olla arvokkaita i18n-skenaarioissa. Tarkastellaan esimerkiksi järjestelmää, jossa käännösavaimet noudattavat tiettyä nimeämiskäytäntöä:
type SupportedLanguages = 'en' | 'es' | 'fr';
type TranslationKeyPrefix = 'common' | 'product' | 'checkout';
type TranslationKey = `${TPrefix}.${string}`;
// Esimerkkikäyttö:
const getTranslation = (key: TranslationKey, lang: SupportedLanguages): string => {
// Simuloi käännöksen hakemista resurssipaketista avaimen ja kielen perusteella
const translations: Record> = {
'common.greeting': {
en: 'Hello',
es: 'Hola',
fr: 'Bonjour',
},
'product.description': {
en: 'A fantastic product!',
es: '¡Un producto fantástico!',
fr: 'Un produit fantastique !',
},
};
const translation = translations[key]?.[lang];
return translation || `Käännöstä ei löytynyt avaimelle: ${key} kielellä: ${lang}`;
};
const englishGreeting = getTranslation('common.greeting', 'en'); // Hello
const spanishDescription = getTranslation('product.description', 'es'); // ¡Un producto fantástico!
const unknownTranslation = getTranslation('nonexistent.key' as TranslationKey, 'en'); // Käännöstä ei löytynyt avaimelle: nonexistent.key kielellä: en
TranslationKey-tyyppi varmistaa, että kaikki käännösavaimet noudattavat yhtenäistä muotoa, mikä yksinkertaistaa käännösten hallintaa ja virheiden ehkäisyä.
Rajoitukset
Vaikka malliliteraalityypit ovat tehokkaita, niillä on myös rajoituksia:
- Monimutkaisuus: Monimutkainen jäsennyslogiikka voi nopeasti muuttua vaikealukuiseksi ja vaikeaksi ylläpitää.
- Suorituskyky: Malliliteraalityyppien laaja käyttö voi vaikuttaa käännösaikaiseen suorituskykyyn, erityisesti suurissa projekteissa.
- Tyyppiturvallisuuden aukot: Kuten sähköpostin validointiesimerkki osoitti, käännösaikaiset tarkistukset eivät aina riitä. Ajonaikainen validointi on edelleen tarpeen tapauksissa, joissa ulkoisen datan on noudatettava tiukkoja muotoja.
Parhaat käytännöt
Jotta voit käyttää malliliteraalityyppejä tehokkaasti, noudata näitä parhaita käytäntöjä:
- Pidä se yksinkertaisena: Pilko monimutkainen jäsennyslogiikka pienempiin, hallittavissa oleviin tyyppeihin.
- Dokumentoi tyyppisi: Dokumentoi selkeästi malliliteraalityyppiesi tarkoitus ja käyttö.
- Testaa tyyppisi: Luo yksikkötestejä varmistaaksesi, että tyyppisi toimivat odotetusti.
- Tasapainota käännös- ja ajonaikainen validointi: Käytä malliliteraalityyppejä perusvalidointiin ja ajonaikaisia tarkistuksia monimutkaisempiin skenaarioihin.
Yhteenveto
TypeScriptin malliliteraalityypit tarjoavat tehokkaan ja joustavan tavan käsitellä merkkijonotyyppejä käännösaikana. Yhdistämällä malliliteraaleja ehdollisiin tyyppeihin ja tyypin päättelyyn voit luoda hienostunutta tyyppitason logiikkaa merkkijonopohjaisen datan jäsentämiseen, validoimiseen ja muuntamiseen. Vaikka huomioon otettavia rajoituksia onkin, malliliteraalityyppien käytön hyödyt tyyppiturvallisuuden ja koodin ylläpidettävyyden kannalta voivat olla merkittäviä.
Hallitsemalla nämä edistyneet tekniikat kehittäjät voivat luoda vankempia ja luotettavampia TypeScript-sovelluksia.
Lisätutkimusta
Syventääksesi ymmärrystäsi malliliteraalityypeistä, harkitse seuraaviin aiheisiin tutustumista:
- Mapped Types: Opi muuntamaan oliotyyppejä malliliteraalityyppien perusteella.
- Aputyypit (Utility Types): Tutustu sisäänrakennettuihin TypeScriptin aputyyppeihin, joita voidaan käyttää yhdessä malliliteraalityyppien kanssa.
- Edistyneet ehdolliset tyypit: Syvenny ehdollisten tyyppien ominaisuuksiin monimutkaisemman tyyppitason logiikan luomiseksi.